#include "I_UnixNetProcessor.h"
#include "P_NetAcceptAdapter.h"
#include <stdio.h>

int ET_NET = ET_CALL;
UnixNetProcessor netProcessor;

static void
init_thread_for_net(EThread *t)
{
	if(netProcessor.net_handler_offset == -1)
	{
		netProcessor.net_handler_offset = eventProcessor.allocate_thread_spec(sizeof(class NetHandler), ET_NET);
		netProcessor.poll_poller_offset = eventProcessor.allocate_thread_spec(sizeof(struct PollPoller), ET_NET);
		netProcessor.event_io_offset = eventProcessor.allocate_thread_spec(sizeof(struct EventIO), ET_NET);
	}

	new(get_EventIO(t))EventIO();
	new(get_NetHandler(t))NetHandler();
	get_NetHandler(t)->mutex = new_ProxyMutex();
	new(get_PollPoller(t))PollPoller();

	t->schedule_imm(get_NetHandler(t));
}

NetHandler *
get_NetHandler(EThread *t)
{
	return (NetHandler *)ETHREAD_GET_PRIVATE(t, netProcessor.net_handler_offset);
}

PollPoller *
get_PollPoller(EThread *t)
{
	return (PollPoller *)ETHREAD_GET_PRIVATE(t, netProcessor.poll_poller_offset);
}

PollDescriptor *
get_PollDescriptor(EThread *t)
{
	PollPoller *poller = get_PollPoller(t);
	return poller->pd;
}

EventIO *
get_EventIO(EThread *t)
{
	return (EventIO *)ETHREAD_GET_PRIVATE(t, netProcessor.event_io_offset);
}

UnixNetProcessor::UnixNetProcessor() : net_inited(false), net_handler_offset(0)
{
	net_handler_offset = -1;
	poll_poller_offset = -1;
	event_io_offset = -1;
}

bool 
UnixNetProcessor::start(int number_of_threads, int detached, size_t stacksize)
{
	(void)detached;
	(void)stacksize;

	if(net_inited)
		return false;

	if(number_of_threads > 0)
	{
		ET_NET = eventProcessor.spawn_event_threads(number_of_threads, "ET_NET");
	}

	for(int i = 0; i < eventProcessor.n_threads_for_type[ET_NET]; i++)
	{
		init_thread_for_net(eventProcessor.event_threads[ET_NET][i]);
	}

	return true;
}

UnixNetVConnect *
UnixNetProcessor::alloc_vc()
{
	return new UnixNetVConnect;
}

Action *
UnixNetProcessor::accept(Continuation *c, int n_threads, const IpEntry &entry, const AcceptOptions &opt)
{
	char thread_name[MAX_THREAD_NAME_LEN];
	//only support mode 1
	assert(n_threads > 0);
	NetAcceptAdapter *na = new NetAcceptAdapter;
	na->_action = c;
	na->server.listen(&entry, opt.sock_type, opt.blocking_accept);
	na->fd_blocking = opt.fd_blocking;

	for(int i = 1; i < n_threads; i++)
	{
		snprintf(thread_name, MAX_THREAD_NAME_LEN - 1, "[ET_ACCEPT %d]", i);
		NetAcceptAdapter *aa = na->clone();
		aa->init_blocking_accept(thread_name);
	}

	na->init_blocking_accept("[ET_ACCEPT 0]");

	return &(na->_action);
}

Action *
UnixNetProcessor::connect_re(Continuation *c, const IpEntry &entry, const NetOptions &opt)
{
	UnixNetVConnect *vc = new UnixNetVConnect;
	vc->mutex = c->mutex;
	SET_CONTINUATION_HANDLER(vc, &UnixNetVConnect::connectEvent);
	vc->conn._remote_entry = entry;
	vc->opt = opt;
	vc->_action = c;

	eventProcessor.schedule_imm(vc, ET_NET);

	return &(vc->_action);
}
